package com.chenc.javathreads.uid.service.impl;

import com.chenc.javathreads.uid.UidGenerator;
import com.chenc.javathreads.uid.service.UidUseService;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName
 * @Describtion: 生成并获取下一个全局流水号
 * @Author:Chenc
 * @Date:2023/12/28 17:57
 * @Version 1.0
 */
@Service
@Slf4j
public class UidUseServiceImpl implements UidUseService {

    private static final int SIZE = 10; // 10w
    private static final boolean VERBOSE = false;
    private static final int THREADS = Runtime.getRuntime().availableProcessors() << 1;

    private ConcurrentSkipListSet<Long> gobalNos1 = new ConcurrentSkipListSet<>();
    private ConcurrentSkipListSet<Long> gobalNos2 = new ConcurrentSkipListSet<>();
    @Resource
    private UidGenerator defaultUidGenerator;

    /**
     * 外部获取下一个全局流水号
     * 需要保证取出变量的动作是同步的
     * 对象是安全的
     * 存入变量的动作是同步的
     *
     * @return
     */
    @Override
    public String getNextGobalNo() {

        if (gobalNos1.size() <= 0) {
            convertGobalNos2TogobalNos1();
            getAndRemoveNext();
        }
        return getAndRemoveNext();
    }

    private String getAndRemoveNext(){
        synchronized(gobalNos1){
            Long ceiling = gobalNos1.ceiling(0L);
            gobalNos1.remove(ceiling);
            return String.valueOf(ceiling);
        }
    }

    private void convertGobalNos2TogobalNos1() {
        synchronized (gobalNos1) {
            if (gobalNos1.size() <= 0) {
                synchronized (gobalNos2) {
                    if (gobalNos2.size() <= 0) {
                        Set<Long> nextSizeGobalNos = this.getNextSizeGobalNos();
                        gobalNos1.addAll(nextSizeGobalNos);
                        Set<Long> nextSizeGobalNos1 = this.getNextSizeGobalNos();
                        gobalNos2.addAll(nextSizeGobalNos1);
                    } else {
                        gobalNos1.addAll(gobalNos2);
                        Set<Long> nextSizeGobalNos1 = this.getNextSizeGobalNos();
                        gobalNos2.addAll(nextSizeGobalNos1);
                    }
                }
            }
        }
    }

    //一次性获取10W的全局流水号
    private Set<Long> getNextSizeGobalNos() {
        try {
            AtomicInteger control = new AtomicInteger(-1);
            Set<Long> uidSet = new ConcurrentSkipListSet<>();

            // Initialize threads
            List<Thread> threadList = new ArrayList<>(THREADS);
            for (int i = 0; i < THREADS; i++) {
                Thread thread = new Thread(() -> workerRun(uidSet, control));
                thread.setName("UID-generator-" + i);

                threadList.add(thread);
                thread.start();
            }

            // Wait for worker done
            for (Thread thread : threadList) {
                thread.join();
            }
            return uidSet;
        } catch (InterruptedException e) {
            log.info("InterruptedException e");
        }
        return null;
    }

    /**
     * Worker run
     */
    private void workerRun(Set<Long> uidSet, AtomicInteger control) {
        for (; ; ) {
            int myPosition = control.updateAndGet(old -> (old == SIZE ? SIZE : old + 1));
            if (myPosition == SIZE) {
                return;
            }

            doGenerate(uidSet, myPosition);
        }
    }


    private void doGenerate(Set<Long> uidSet, int index) {
        long uid = defaultUidGenerator.getUID();
        String parsedInfo = defaultUidGenerator.parseUID(uid);
        uidSet.add(uid);

        if (VERBOSE) {
            log.info(Thread.currentThread().getName() + " No." + index + " >>> " + parsedInfo);
        }
    }

}
